/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include "board.h"
#include "hpm_debug_console.h"
#include "hpm_spi_drv.h"
#include "hpm_clock_drv.h"
#include "hpm_gpio_drv.h"

#include "hpm_gpio_drv.h"
#include "hpm_gpiom_drv.h"
typedef enum {
    spi_op_write = 0,
    spi_op_read,
    spi_op_no_data
} spi_op_t;

#define PROGRAM_LOAD    0x02
#define WRITE_ENABLE    0x06
#define READ_FROM_CACHE 0x03


uint8_t test_data[2048] = {0};
uint8_t read_data[2048] = {0};
uint8_t program_chahe[2048+256] = {0};

void test_data_make(void)
{
    for(uint32_t i = 0;i<2048;i++)
    {
        test_data[i] = (uint8_t)i;
    }
}
uint16_t read_nand_id(SPI_Type* base);
uint32_t nand_flash_write(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len);
uint32_t nand_flash_read(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len);
void nand_flash_init(SPI_Type* base);

int main(void)
{
    uint8_t wbuff[4] = {0x9f, 0x00, 0x00,0x00};
    uint8_t rbuff[4] = {0};

    spi_timing_config_t timing_config = {0};
    spi_format_config_t format_config = {0};
    spi_control_config_t control_config = {0};
    hpm_stat_t stat;
    uint32_t spi_clcok;

    /* bsp initialization */
    board_init();
    spi_clcok = board_init_spi_clock(BOARD_APP_SPI_BASE);
    board_init_spi_pins(BOARD_APP_SPI_BASE);
    
    printf("SPI-Master Polling Transfer Example\n");

    /* set SPI sclk frequency for master */
    spi_master_get_default_timing_config(&timing_config);
    timing_config.master_config.clk_src_freq_in_hz = spi_clcok;
    timing_config.master_config.sclk_freq_in_hz = BOARD_APP_SPI_SCLK_FREQ;
    if (status_success != spi_master_timing_init(BOARD_APP_SPI_BASE, &timing_config)) {
        printf("SPI master timming init failed\n");
        while (1) {
        }
    }
    printf("SPI-Master transfer timing is configured.\n");
    printf("SPI-Master transfer source clock frequency: %dHz\n", timing_config.master_config.clk_src_freq_in_hz);
    printf("SPI-Master transfer sclk frequency: %dHz\n", timing_config.master_config.sclk_freq_in_hz);

    /* set SPI format config for master */
    spi_master_get_default_format_config(&format_config);
    format_config.common_config.data_len_in_bits = 8;/* 8 bit */
    format_config.common_config.mode = spi_master_mode;
    format_config.common_config.cpol = spi_sclk_high_idle;
    format_config.common_config.cpha = spi_sclk_sampling_even_clk_edges;
    spi_format_init(BOARD_APP_SPI_BASE, &format_config);
    printf("SPI-Master transfer format is configured.\n");

    /* set SPI control config for master */
    spi_master_get_default_control_config(&control_config);
    control_config.master_config.cmd_enable = false;  /* cmd phase control for master */
    control_config.master_config.addr_enable = false; /* address phase control for master */
    control_config.common_config.trans_mode = spi_trans_write_dummy_read;
    control_config.common_config.dummy_cnt = spi_dummy_count_1;

    test_data_make();
    
    nand_flash_init(BOARD_APP_SPI_BASE);
    nand_flash_write(BOARD_APP_SPI_BASE,0,0,0,test_data,sizeof(test_data));
    nand_flash_read(BOARD_APP_SPI_BASE,0,0,0,read_data,256);


    while(1) {
    }

    return 0;
}

uint32_t Spi_Transmit(SPI_Type* base,uint8_t* write_buf,uint8_t* read_buf,uint32_t write_count,uint32_t read_count,uint8_t dummy_count)
{
    printf("write count = %d\r\n",write_count);
    hpm_stat_t stat;
    spi_control_config_t control_config = {0};
    /* set SPI control config for master */
    spi_master_get_default_control_config(&control_config);
    control_config.master_config.cmd_enable = false;  /* cmd phase control for master */
    control_config.master_config.addr_enable = false; /* address phase control for master */
    
    switch (dummy_count) {
        case 0:
            control_config.common_config.trans_mode = spi_trans_write_read;
            break;
        case 1:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        case 2:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        case 3:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        default:
            printf("dummy too much\r\n");
            return status_invalid_argument;
        break;
    }
    if(read_count == 0)
    {
        control_config.common_config.trans_mode = spi_trans_write_only;
    }
    stat = spi_transfer(base,
                &control_config,
                NULL, NULL,
                (uint8_t *)write_buf, write_count, (uint8_t *)read_buf,read_count);
    return stat;
}

uint8_t Get_Nand_Status(SPI_Type* base,uint8_t status_addr)
{
    uint8_t cmd[2] = {0x0f,status_addr};
    uint8_t recive_data = 0;
    Spi_Transmit(base, cmd, &recive_data,2,1,0);
    return recive_data;
}

/**
 * @brief 
 */
uint32_t nand_flash_write(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len)
{
    uint8_t write_enable = 0x06;
    uint8_t write_cmd[3] = {0};
    uint8_t program_execute_cmd[4] = {0};

    uint16_t program_times = 0;

    uint8_t status = 0;

    uint32_t program_execute = (block << 6) |(page&0x3f);

    write_cmd[0] = 0x02;
    write_cmd[1] = (col & 0x0F00) >> 8;;
    write_cmd[2] = col & 0xFF;

    memcpy(program_chahe, write_cmd, sizeof(write_cmd));
    memcpy(&program_chahe[sizeof(write_cmd)], buf,len);

    
    Spi_Transmit(base, program_chahe, NULL,256+sizeof(write_cmd),0,0);/* 发02 */
    Spi_Transmit(base, &write_enable, NULL,sizeof(write_enable),0,0);/* 发06 写使能 */

    
    program_execute_cmd[0] = 0x10;				/* program execute 0x10 */
    program_execute_cmd[1] = (program_execute & 0xFF0000) >> 16;
    program_execute_cmd[2] = (program_execute & 0xFF00) >> 8;
    program_execute_cmd[3] = program_execute & 0xFF;
    
    Spi_Transmit(base, program_execute_cmd, NULL,sizeof(program_execute_cmd),0,0);/* 发10 将缓冲区的数据写入nand */
    
    while(1){
        status = Get_Nand_Status(base,0xc0);/* 查看写到缓冲区的内容是否正确 */
        if((status&0x04) == 0)
        {
            return status_success;
        }
        program_times++;
        if(program_times >50)
        {
            break;
        }
    }
    return status_fail;
}

uint32_t nand_flash_read(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len)
{
    uint32_t ui32Addr = (block << 6) + page;	
    uint8_t read_page_to_cache[4] = {0};
    uint8_t read_from_cache[3] = {0};
    uint8_t status = 0;
    uint16_t loop_times = 0;
    read_page_to_cache[0] = 0x13;
    read_page_to_cache[1] = (uint8_t)(ui32Addr >> 16);
    read_page_to_cache[2] = (uint8_t)(ui32Addr >> 8);
    read_page_to_cache[3] = (uint8_t)ui32Addr;

    read_from_cache[0] = 0x03;
    read_from_cache[1] = (col & 0xFF00)>>8;
    read_from_cache[2] = col&0xff;

    Spi_Transmit(base, read_page_to_cache, NULL,sizeof(read_page_to_cache),0,0);/* 命令nand将该page的数据写入cache */

    while(1){
        status = Get_Nand_Status(base,0xc0);/* 查看写到缓冲区的内容是否正确 */
        if((status&0x01) == 0)
        {
            break;
        }
        loop_times++;
        if(loop_times >50)
        {
            printf("Nand read page to cache failed\r\n");
            return status_fail;
        }
    }
    Spi_Transmit(base, read_from_cache, buf,sizeof(read_from_cache),len,1);/* 命令nand将该page的数据写入cache */
}

uint16_t read_nand_id(SPI_Type* base)
{
    uint8_t wbuff[1] = {0x9f};
    uint16_t rbuff = {0};

    Spi_Transmit(base, wbuff, (uint8_t*)&rbuff,sizeof(wbuff),2,1);
    return rbuff;
}

void nand_flash_init(SPI_Type* base)
{
    uint8_t reset_cmd = 0xff;
    uint16_t nand_id = 0;
    uint8_t ui8Feature = 0 			|
					//	0x01 << 7	|	// OTP_PRT
					//	0x01 << 6	|	// OTP_EN
						0x01 << 4	|	// ECC_EN
					//	0x01 << 0	|	// QE,quad IO operations can be executed
						0x00;
    uint8_t status = 0;
    uint8_t block_protection_unlock[3] = {0x1f,0xa0,0x00};
    uint8_t block_ecc_en[3] = {0x1f,0xb0,ui8Feature};
    Spi_Transmit(base, &reset_cmd, NULL,sizeof(reset_cmd),0,0);/* 复位nand */
    nand_id = read_nand_id(base);/*读nand id*/
    printf("nand id = 0x%x\r\n",nand_id);

    Spi_Transmit(base, block_protection_unlock, NULL,sizeof(block_protection_unlock),0,0);/* 解除保护 */

    status = Get_Nand_Status(base,0xc0);/*  */
    printf("address C0 value is=%x\r\n",status);

    Spi_Transmit(base, block_ecc_en, NULL,sizeof(block_ecc_en),0,0);/* 解除保护 */

    status = Get_Nand_Status(base,0xb0);/*  */
    printf("address B0 value is=%x\r\n",status);
}